package org.mte.sak.util;

import static java.util.Calendar.APRIL;
import static java.util.Calendar.AUGUST;
import static java.util.Calendar.DAY_OF_MONTH;
import static java.util.Calendar.DECEMBER;
import static java.util.Calendar.JANUARY;
import static java.util.Calendar.JUNE;
import static java.util.Calendar.MARCH;
import static java.util.Calendar.MAY;
import static java.util.Calendar.MONTH;
import static java.util.Calendar.NOVEMBER;
import static java.util.Calendar.SATURDAY;
import static java.util.Calendar.SUNDAY;
import static java.util.Calendar.YEAR;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;


/**
 * Timer
 *
 * @author Marco Traversari
 * @version $Revision: 1.5 $ $Date: 2012-03-30 08:48:50 $
 */
public class Timer {

    /**
     * EQUAL_TO
     */
	public static final String E = "=";

    /**
     * GREATER_THAN
     */
	public static final String G = ">";

    /**
     * LESS_THAN
     */
	public static final String L = "<";

    /**
     * GREATER_THAN_OR_EQUAL_TO
     */
	public static final String GE = ">=";

    /**
     * LESS_THAN_OR_EQUAL_TO
     */
	public static final String LE = "<=";

    /**
     * NOT_EQUAL_TO
     */
	public static final String NE = "!=";

	/**
	 * Millisecondi presenti in un giorno.
	 */
	public static final long ONE_DAY_MILLISECONDS = 86400000;

	/**
	 * Giorni di vacanza
	 */
	public static final String[] DEFAULT_HOLIDAYS = {
		JANUARY + "-" + 1,
		JANUARY + "-" + 6,
		APRIL + "-" + 25,
		MAY + "-" + 1,
		JUNE + "-" + 2,
		AUGUST + "-" + 15,
		NOVEMBER + "-" + 1,
		DECEMBER + "-" + 8,
		DECEMBER + "-" + 25,
		DECEMBER + "-" + 26};

	/**
	 * holidays
	 */
	private String[] holidays = DEFAULT_HOLIDAYS;

	/**
	 * Mappa di anni e giorni di vacanza
	 */
	private Map<Integer, List<Calendar>> mapHolidays = null;

	/**
	 * startTime
	 */
	private long startTime = 0;

	/**
	 * stopTime
	 */
	private long stopTime = 0;

	/**
	 * elapsed
	 */
	private long elapsed  = 0;

	/**
	 * SimpleDateFormat
	 */
	private SimpleDateFormat sdf = null;

	/**
	 * pattern
	 */
	private String pattern = null;

	/**
	 * locale
	 */
	private Locale locale = null;

	/**
	 * Timer
	 */
	public Timer() {
		initFormatter(null, null);
	}

	/**
	 * Timer
	 * @param pattern
	 */
	public Timer(String pattern) {
		initFormatter(pattern, null);
	}

	/**
	 * Timer
	 * @param pattern
	 * @param locale
	 */
	public Timer(String pattern, Locale locale) {
		this.locale = locale;
		initFormatter(pattern, locale);
	}

	/**
	 * Timer
	 * @param pattern
	 * @param locale
     * @param holidays è un array di stringhe dove la singola stringa
     * 		è ad esempio "Calendar.MAY-1" ossia "4-1" che indica
     * 		una festività nell'anno.
	 */
	public Timer(String pattern, Locale locale, String[] holidays) {
		this.locale = locale;
		initFormatter(pattern, locale);
		this.holidays = holidays;
	}

	/**
	 * initFormatter
	 * @param pattern
	 * @param locale
	 */
	private void initFormatter(String pattern, Locale locale) {
		if (pattern == null && locale == null) {
			sdf = new SimpleDateFormat();
		} else if (pattern != null && locale == null) {
			this.pattern = pattern;
	        sdf = new SimpleDateFormat(pattern);
		} else if (pattern != null && locale != null) {
			this.pattern = pattern;
			this.locale = locale;
			sdf = new SimpleDateFormat(pattern, locale);
		}
		if (sdf != null) {
			sdf.setLenient(false);
		}
	}

	/**
	 * applyPattern
	 * @param pattern
	 * @deprecated use setPattern
	 */
	public void applyPattern(String pattern) {
		setPattern(pattern);
	}

	/**
	 * getPattern
	 * @return pattern
	 */
	public String getPattern() {
		return pattern;
	}

	/**
	 * setPattern
	 * @param pattern
	 */
	public void setPattern(String pattern) {
		this.pattern = pattern;
		sdf.applyPattern(pattern);
	}

	/**
	 * getLocale
	 * @return locale
	 */
	public Locale getLocale() {
		return locale;
	}

	/**
	 * Imposta il locale: questo avviene
	 * se il pattern è valorizzato in modo che
	 * l'oggetto "SimpleDateFormat" venga
	 * ristanziato correttamente.
	 *
	 * Se il pattern non è valorizzato non
	 * si ha nessuno effetto
	 */
	public void setLocale(Locale locale) {
		if (pattern != null) {
			this.locale = locale;
			sdf = new SimpleDateFormat(pattern, locale);
			sdf.setLenient(false);
		}
	}

    /**
     * Restiuisce un stringa con la data corrente nel formato specificato
     * nell'oggetto.
     *
     * @return la data corrente.
     */
	public String now() {
		return sdf.format(new Date(System.currentTimeMillis()));
	}

    /**
     * Restiuisce un stringa con la data corrente nel formato specificato.
     *
     * @param pattern è il formato della data.
     * @return la data corrente.
     */
	public String now(String pattern) {
		sdf.applyPattern(pattern);
		return sdf.format(new Date(System.currentTimeMillis()));
	}

    /**
     * Converte una oggetto <code>Date</code> in una stringa secondo il formato
     * dell'oggetto.
     *
     * @param date è la data espressa in <code>Date</code>.
     * @return una stringa rappresentante la data.
     */
	public String getString(Date date) {
		return sdf.format(date);
	}

    /**
     * Converte una oggetto <code>Date</code> in una stringa secondo il formato
     * espresso.
     *
     * @param date è la data espressa in <code>Date</code>.
     * @param pattern è il formato della data.
     * @return una stringa rappresentante la data.
     */
    public String getString(Date date, String pattern) {
    	sdf.applyPattern(pattern);
        return sdf.format(date);
    }

    /**
     * Converte una stringa rappresentante una data in un oggetto
     * di tipo <code>Date</code> secondo il formato dell'oggetto.
     *
     * @param strDate è una stringa rappresentante la data.
     *
     * @return un oggetto di tipo <code>Date</code>.
     * @throws ParseException se si verificano problemi di parse
     */
    public Date getDate(String strDate) throws ParseException {
        return sdf.parse(strDate);
    }

    /**
     * Converte una stringa rappresentante una data in un oggetto
     * di tipo <code>Date</code> secondo il formato espresso.
     *
     * @param strDate è una stringa rappresentante la data.
     * @param pattern è il formato della data espressa in <code>strDate</code>.
     *
     * @return un oggetto di tipo <code>Date</code>.
     * @throws ParseException se si verificano problemi di parse
     */
    public Date getDate(String strDate, String pattern) throws ParseException {
    	sdf.applyPattern(pattern);
        return sdf.parse(strDate);
    }

    /**
     * Aggiunge alla data specificata il numero di giorni indicato.
     *
     * @param date data iniziale.
     * @param days numero giorni.
     * @return date
	 * @deprecated by addDays(Date date, int days)
     */
    public static Date add(Date date, int days) {
    	return addDays(date, days);
    }

    /**
     * addSeconds
     * @param date
     * @param seconds
     * @return date
     */
    public static Date addSeconds(Date date, int seconds) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.SECOND, seconds);
        return calendar.getTime();
    }

    /**
     * addMinutes
     * @param date
     * @param minutes
     * @return date
     */
    public static Date addMinutes(Date date, int minutes) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MINUTE, minutes);
        return calendar.getTime();
    }

    /**
     * addHours
     * @param date
     * @param hours
     * @return date
     */
    public static Date addHours(Date date, int hours) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR, hours);
        return calendar.getTime();
    }

    /**
     * Aggiunge alla data specificata il numero di giorni indicato.
     *
     * @param date data iniziale.
     * @param days numero giorni.
     * @return date
     */
    public static Date addDays(Date date, int days) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, days);
        return calendar.getTime();
    }

    /**
     * addYears
     * @param date
     * @param years
     * @return date
     */
    public static Date addYears(Date date, int years) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.YEAR, years);
        return calendar.getTime();
    }

    /**
     * addMonths
     * @param date
     * @param months
     * @return date
     */
    public static Date addMonths(Date date, int months) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, months);
        return calendar.getTime();
    }

    /**
     * <p>Aggiunge 1 giorno lavorativo alla data passata in input.</p>
     *
     * @param date la data iniziale
     * @param everyDays lista giorni della settimana non lavorativi.
     * @param holidays date delle festività.
     * @return la data iniziale aumentata di un giorno lavorativo
     * @deprecated TODO
     */
	public static Date addWorkDay(Date date, int[] everyDays, Date[] holidays) {
		Date workDay = addDays(date, 1);
		if (isHoliday(workDay, everyDays, holidays)) {
			workDay = addWorkDay(workDay, everyDays, holidays);
		}
		return workDay;
	}

    /**
     * <p>Calcola i giorni tra due date.</p>
     *
     * <p>Se date1 > date2 il valore sarà negativo.</p>
     *
     * @param date1 la prima data.
     * @param date2 la seconda data.
     * @return i giorni tra le due date.
     */
    public static int calcDays(Date date1, Date date2) {
        Calendar d1 = Calendar.getInstance();
        d1.setTime(date1);
        Calendar d2 = Calendar.getInstance();
        d2.setTime(date2);
        boolean isNegative = false;
        if (d1.after(d2)) {
            Calendar swap = d1;
            d1 = d2;
            d2 = swap;
            isNegative = true;
        }
        int days = d2.get(Calendar.DAY_OF_YEAR) -
                   d1.get(Calendar.DAY_OF_YEAR);
        int y2 = d2.get(Calendar.YEAR);
        if (d1.get(Calendar.YEAR) != y2) {
            d1 = (Calendar) d1.clone();
            do {
                days += d1.getActualMaximum(Calendar.DAY_OF_YEAR);
                d1.add(Calendar.YEAR, 1);
            } while (d1.get(Calendar.YEAR) != y2);
        }
        days = (isNegative) ? -days : days;
        return days;
    }

    /**
     * <p>Calcola i giorni lavorativi tra due date.</p>
     *
     * <p>In <code>everyList</code> sono presenti
     * i nomi dei giorni della settimana per i quali non si lavora</p>
     *
     * <p>In <code>holidayList</code> sono presenti le date delle festività e delle ferie.</p>
     *
     * @param date1 data inzio.
     * @param date2 data fine.
     * @param everyList lista giorni della settimana non lavorativi.
     * @param holidayList date delle festività.
     * @return il numero dei giorni lavorativi nell'intervallo.
     * @deprecated by calcWorkDays(Date date0, Date date1)
     */
    public static int calcWorkDays(Date date1, Date date2, ArrayList<Integer> everyList, ArrayList<Date> holidayList) {
    	int[] everyDays = new int[everyList.size()];
    	int i = 0;
    	for (int everyInt: everyList) {
    		everyDays[i] = everyInt;
    		i++;
    	}
    	Date[] holidays = new Date[holidayList.size()];
    	i = 0;
    	for (Date holiday: holidays) {
    		holidays[i] = holiday;
    		i++;
    	}
    	return calcWorkDays(date1, date2, everyDays, holidays);
    }

    /**
     * <p>Calcola i giorni lavorativi tra due date.</p>
     *
     * <p>In <code>everyDays</code> sono presenti i numeri relativi
     * ai giorni della settimana per i quali non si lavora (es. Calendar.SATURDAY, Calendar.SUNDAY)</p>
     *
     * <p>In <code>holidays</code> sono presenti le date delle festività e delle ferie.</p>
     *
     * @param date1 data inzio.
     * @param date2 data fine.
     * @param everyDays lista giorni della settimana non lavorativi.
     * @param holidays date delle festività.
     * @return il numero dei giorni lavorativi nell'intervallo.
     * @deprecated by calcWorkDays(Date date0, Date date1)
     */
    public static int calcWorkDays(Date date1, Date date2, int[] everyDays, Date[] holidays) {
        int workDays = 0;
        int days = calcDays(date1, date2);
        //System.out.println("days=" + days);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date1);
        int everyListSize = (everyDays != null) ? everyDays.length : 0;
        int holidayListSize = (holidays != null) ? holidays.length : 0;
        for (int d = 1; d < days + 1; d++) {
        	calendar.add(Calendar.DAY_OF_MONTH, 1);
        	//System.out.println("date=" +  calendar.getTime());
        	boolean isHoliday = false;
        	int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        	//System.out.println("dayOfWeek=" + dayOfWeek);
            for (int e = 0; e < everyListSize; e++) {
                if (dayOfWeek == everyDays[e]) {
                    isHoliday = true;
                }
            }
            for (int l = 0; !isHoliday && l < holidayListSize; l++) {
            	//System.out.println("calendar.getTime()" + calendar.getTime() + "-holidays[l]=" + holidays[l]);
                if (calendar.getTime().compareTo(holidays[l]) == 0) {
                    isHoliday = true;
                }
            }
            if (!isHoliday) {
                workDays++;
            }

        }
        return workDays;
    }

    /**
	 * Calcola i giorni lavorativi tra due date.
	 *
	 * Comincia a contare da date0 + 1 e comprende
	 * anche date1 se questo è un giorno lavorativo
	 *
	 * @param date0
     * @param date1
     * @return workDays
     */
    public int calcWorkDays(Date date0, Date date1) {
        int workDays = 0;
        int days = calcDays(date0, date1);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date0);
        for (int d = 1; d < days + 1; d++) {
        	calendar.add(Calendar.DAY_OF_MONTH, 1);
        	if (isWorkDay(calendar)) {
        		workDays++;
        	}
        }
        return workDays;
    }

    /**
     * <p>Controlla se la data passata è un giorno di vacanza.</p>
     *
     * @param date la data da controllare.
     * @param everyDays lista giorni della settimana non lavorativi.
     * @param holidays date delle festività
     * @return true se è un giorno di vacanza.
     * @deprecated by isWorkDay
     */
    public static boolean isHoliday(Date date, int[] everyDays, Date[] holidays) {
    	boolean isHoliday = false;
        int everyListSize = (everyDays != null) ? everyDays.length : 0;
        int holidayListSize = (holidays != null) ? holidays.length : 0;
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
        for (int e = 0; e < everyListSize; e++) {
            if (dayOfWeek == everyDays[e]) {
                isHoliday = true;
            }
        }
        for (int l = 0; !isHoliday && l < holidayListSize; l++) {
        	//System.out.println("calendar.getTime()" + calendar.getTime() + "-holidays[l]=" + holidays[l]);
            if (calendar.getTime().compareTo(holidays[l]) == 0) {
                isHoliday = true;
            }
        }
        return isHoliday;
    }

    /**
     * isWorkDay
     * @param date
     * @return isWorkDay
     */
    public boolean isWorkDay(Date date) {
    	Calendar calendar = Calendar.getInstance();
    	calendar.setTime(date);
    	return isWorkDay(calendar);
    }

    /**
     * isWorkDay
     * @param calendar
     * @return isWorkDay
     */
    public boolean isWorkDay(Calendar calendar) {
    	boolean isWorkDay = false;
    	if (mapHolidays == null) {
    		mapHolidays = new HashMap<Integer, List<Calendar>>();
    	}
		int year = calendar.get(Calendar.YEAR);
		if (!mapHolidays.containsKey(year)) {
			mapHolidays.put(year, calcHolidayCalendars(year, holidays, true));
		}
		List<Calendar> holidayCalendars = mapHolidays.get(year);


		int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
		boolean isWeekend = dayOfWeek == SATURDAY || dayOfWeek == SUNDAY;
		boolean isHoliday = isHoliday(holidayCalendars, calendar);

		/*
		 * isWeekend	isHoliday	isWorDay
		 * 	true		true		false
		 * 	true		false		false
		 *  false		true		false
		 *  false		false		true
		 */
		isWorkDay = !isWeekend && !isHoliday;
		return isWorkDay;
    }

    /**
     * isHoliday
     * @param holidayCalendars
     * @param calendar
     * @return isHoliday
     */
	private boolean isHoliday(List<Calendar> holidayCalendars, Calendar calendar) {
		boolean isHoliday = false;
		Iterator<Calendar> iterator = holidayCalendars.iterator();
		while (iterator.hasNext() && !isHoliday) {
			Calendar holidayCalendar = iterator.next();
			isHoliday = calendar.get(YEAR) == holidayCalendar.get(YEAR) &&
				calendar.get(MONTH) == holidayCalendar.get(MONTH) &&
				calendar.get(DAY_OF_MONTH) == holidayCalendar.get(DAY_OF_MONTH);
		}
		return isHoliday;
	}

	/**
	 * calcHolidayCalendars
	 * @param year
	 * @param holidays
	 * @param isAddEasterMonday
	 * @return holidayCalendars
	 */
    private List<Calendar> calcHolidayCalendars(int year, String[] holidays, boolean isAddEasterMonday) {
    	List<Calendar> holidayCalendars = null;
    	if (holidays != null) {
    		holidayCalendars = new ArrayList<Calendar>();
	    	for (String holiday : holidays) {
	    		String[] tks = holiday.split("-");
	    		int month = Integer.parseInt(tks[0]);
	    		int day = Integer.parseInt(tks[1]);
	    		Calendar baseCalendar = Calendar.getInstance();
	    		baseCalendar.set(year, month, day);
	    		holidayCalendars.add(baseCalendar);
	    	}
	    	if (isAddEasterMonday) {
	    		Calendar easterDate = findEasterDate(year);
	    		Calendar baseCalendar = Calendar.getInstance();
	    		baseCalendar.set(year, easterDate.get(MONTH), easterDate.get(DAY_OF_MONTH) + 1);
	    		holidayCalendars.add(baseCalendar);
	    	}
    	}
    	return holidayCalendars;
    }

	public Calendar findEasterDate(int year) {
		Calendar calendar = null;
		if (year > 1573 && year < 2499) {
			int a = year % 19;
			int b = year % 4;
			int c = year % 7;

			int m = 0;
			int n = 0;

			if ((year >= 1583) && (year <= 1699)) {
				m = 22;
				n = 2;
			}
			if ((year >= 1700) && (year <= 1799)) {
				m = 23;
				n = 3;
			}
			if ((year >= 1800) && (year <= 1899)) {
				m = 23;
				n = 4;
			}
			if ((year >= 1900) && (year <= 2099)) {
				m = 24;
				n = 5;
			}
			if ((year >= 2100) && (year <= 2199)) {
				m = 24;
				n = 6;
			}
			if ((year >= 2200) && (year <= 2299)) {
				m = 25;
				n = 0;
			}
			if ((year >= 2300) && (year <= 2399)) {
				m = 26;
				n = 1;
			}
			if ((year >= 2400) && (year <= 2499)) {
				m = 25;
				n = 1;
			}

			int d = (19 * a + m) % 30;
			int e = (2 * b + 4 * c + 6 * d + n) % 7;

			calendar = new GregorianCalendar();
			calendar.set(Calendar.YEAR, year);

			if (d + e < 10) {
				calendar.set(YEAR, year);
				calendar.set(MONTH, MARCH);
				calendar.set(DAY_OF_MONTH, d + e + 22);
			} else {
				calendar.set(MONTH, APRIL);
				int day = d + e - 9;
				if (26 == day) {
					day = 19;
				}
				if ((25 == day) && (28 == d) && (e == 6) && (a > 10)) {
					day = 18;
				}
				calendar.set(DAY_OF_MONTH, day);
			}
		}
		return calendar;
	}


    /**
     * <p>Verifica che l'anno sia bisesistle</p>
     * <p>Gli anni bisestili sono quelli divisibili per 4, esclusi gli anni secolari
         che sono bisestili soltanto se divisibili per 400.</p>
     * @param year
     * @return true se l'anno è bisesistile
     */
    public static boolean isLeapYear(int year) {
        return (year % 4 == 0 && ((year % 100 != 0) || (year % 400 == 0)));
    }

    /**
     * <p>Compara due date.<p>
     * <p>Restituisce:</p>
     *
     * <ul>
     *   <li>1 se date1 > date2</li>
     *   <li>0 se date1 = date2</li>
     *   <li>-1 se date1 < date2</li>
     * </ul>
     *
     * @param date1 la prima data
     * @param date2 la seconda data
     * @return -1, 0, 1.
     */
    public static int compare(Date date1, Date date2) {
        Long long1 = new Long(date1.getTime());
        Long long2 = new Long(date2.getTime());
        return long1.compareTo(long2);
    }

	/**
     * Compara due date utilizzando l'operatore specificato.
     * L'operatore è rappresentatato da una stringa.
     *
     * @param date1 la prima data
     * @param operator ha valori possibili in (=, >, <, >=, <=, !=)
     * @param date2 la seconda data
     * @return true se la condizione espressa da
     *          <code>date1</code> <code>operator</code> <code>date2</code> � vera.
	 */
    public static boolean compare(Date date1, String operator, Date date2) {
        boolean result = false;
        if (date1 != null && date2 != null && operator != null) {
            result = evaluate(compare(date1, date2), operator);
        }
        return result;
    }

    /**
     * Valuta l'intero ricavato da una comparazione tra date in funzione
     * dell'operatore specificato.
     *
     * @param result
     * @param operator ha valori possibili in (=, >, <, >=, <=, !=)
     * @return true, false.
     */
    private static boolean evaluate(int result, String operator) {
        boolean isTest0 = (result == 0) &&
        		(operator.equals(E) ||
                 operator.equals(GE) ||
                 operator.equals(LE));


        boolean isTest1 = (result > 0) &&
        				(operator.equals(GE) ||
        				 operator.equals(G));

        boolean isTest2 = (result < 0) &&
        			(operator.equals(LE) ||
                	 operator.equals(L));

        boolean isTest3 = (result > 0 || result < 0) &&
        				  (operator.equals(NE));

        return isTest0 || isTest1 || isTest2 || isTest3;
    }

	/**
	 * calcAge
	 * @param birthDate
	 * @return age
	 */
	public static int calcAge(Date birthDate) {
		Calendar birthDay = Calendar.getInstance();
		birthDay.setTime(birthDate);
		Calendar calendar = Calendar.getInstance();
		return calcAge(birthDay, calendar);
	}

	/**
	 * calcAge
	 * @param birthDate
	 * @param date
	 * @return age
	 */
	public static int calcAge(Date birthDate, Date date) {
		Calendar birthDay = Calendar.getInstance();
		birthDay.setTime(birthDate);
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		return calcAge(birthDay, calendar);
	}

	/**
	 * calcAge
	 * @param birthDay
	 * @param calendar
	 * @return age
	 */
	public static int calcAge(Calendar birthDay, Calendar calendar) {
		int ageCalc = -1;
		ageCalc = calendar.get(Calendar.YEAR) - birthDay.get(Calendar.YEAR);
		if (!isAlreadyBirthDay(birthDay, calendar)) {
			ageCalc--;
		}
		return ageCalc;
	}

	/**
	 * isAlreadyBirthDay: controllo se il compleanno è già avvenuto nell'anno
	 * @param birthDay
	 * @param calendar
	 * @return true se il compleanno è già avvenuto
	 */
	private static boolean isAlreadyBirthDay(Calendar birthDay, Calendar calendar) {
		boolean isAlreadyBirthDay = false;
		int year = calendar.get(Calendar.YEAR);
		int month = birthDay.get(Calendar.MONTH);
		int day = birthDay.get(Calendar.DAY_OF_MONTH);
		Calendar normalizedBirthDay = Calendar.getInstance();
		normalizedBirthDay.set(year, month, day, 0, 0, 0);
		Calendar normalizedToday = Calendar.getInstance();
		normalizedToday.set(year, calendar.get(Calendar.MONTH), calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 15);
		Date normalizedBirthDate = normalizedBirthDay.getTime();
		Date normalizedTodayDate = normalizedToday.getTime();
		isAlreadyBirthDay = Timer.compare(normalizedTodayDate, Timer.G, normalizedBirthDate);
		return isAlreadyBirthDay;
	}

    /**
     * <p>Costruisce un oggetto <code>HashMap</code>
     * con i giorni della settimana.</p>
     * <p>I giorni sono nella lingua del <code>Locale</code>
     * passato in input.</p>
     * <p>La chiave della HashMap è data da il numero del giorno
     * e il valore dal nome del giorno nel locale corrente.</p>
     *
     * <p>È necessario aver impostato un <code>locale</code>
     * per questo oggetto.</p>
     *
     * @return hashmap dei giorni della settimana
     */
    public HashMap<Integer, String> getWeek() {
    	HashMap<Integer, String> hm = null;
    	if (locale != null) {
	        Calendar calendar = Calendar.getInstance(locale);
	        calendar.setTimeInMillis(System.currentTimeMillis());
	        hm = new HashMap<Integer, String>();
	        int day = calendar.get(Calendar.DAY_OF_WEEK);
	        calendar.add(Calendar.DAY_OF_MONTH, 1 - day);
	        sdf.applyPattern("EEEE");
	        for (int i = 1; i < 8; i++) {
	            String dayOfWeek = sdf.format(calendar.getTime());
	            calendar.add(Calendar.DAY_OF_MONTH, 1);
	            hm.put(i, dayOfWeek);
	        }
    	}
        return hm;
    }

    /**
	 * getStartTime
	 * @return startTime
	 */
	public long getStartTime() {
		return startTime;
	}

	/**
	 * getStopTime
	 * @return stopTime
	 */
	public long getStopTime() {
		return stopTime;
	}

	/**
	 * getElapsed
	 * @return elapsed
	 */
	public long getElapsed() {
		return elapsed;
	}

    /**
     * Avvia cronometro
     */
    public void start() {
    	start("");
    }

    /**
     * Ferma cronometro
     */
   public void stop() {
	   stop("");
   }

   /**
    * start
    * @param label
    */
   public String start(String label) {
	   startTime = System.currentTimeMillis();
	   return label + "timer=start;time=" + getString(new Date(startTime));
   }
   
   /**
    * stop
    * @param label
    */
   public String stop(String label) {
	   stopTime = System.currentTimeMillis();
	   elapsed = stopTime - startTime;
   	   return label + "timer=stop;time=" + getString(new Date(stopTime)) + ";elapsed=" + elapsed;
   }
}
